/*******************************************************************************
* Copyright (c) 2008, Jeferson Jose de Miranda                                 *
* UDESC - Universidade do Estado de Santa Catarina                             *
* All rights reserved.                                                         *
*                                                                              *
* Redistribution and use in source and binary forms, with or without           *
* modification, are permitted provided that the following conditions are met:  *
*     * Redistributions of source code must retain the above copyright         *
*       notice, this list of conditions and the following disclaimer.          *
*     * Redistributions in binary form must reproduce the above copyright      *
*       notice, this list of conditions and the following disclaimer in the    *
*       documentation and/or other materials provided with the distribution.   *
*     * Neither the name of the Universidade do Estado de Santa Catarina nor   *
*       the names of its contributors may be used to endorse or promote        *
*       products derived from this software without specific prior written     *
*       permission.                                                            *
*                                                                              *
* THIS SOFTWARE IS PROVIDED BY JEFERSON JOSE DE MIRANDA "AS IS" AND ANY        *
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED    *
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE       *
* DISCLAIMED. IN NO EVENT SHALL JEFERSON JOSE DE MIRANDA BE LIABLE FOR ANY     *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES   *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  *
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF     *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            *
*******************************************************************************/

/**
 * \file RGBColor.hpp
 *
 * \brief Encapsulates the RGB color values and related setter and getter
 * methods.
 *
 * \author Jeferson Jose de Miranda
 */

#ifndef COLOR_HPP
#define COLOR_HPP

// (C++) iostream will handle possible output errors.
#include <iostream>

/**
* \def MI_BLACK
* Defines black color.
*/
#define MI_BLACK "000000"

/**
* \def MI_BLUE
* Defines blue color.
*/
#define MI_BLUE "0000FF"

/**
* \def MI_GREEN
* Defines green color.
*/
#define MI_GREEN "00FF00"

/**
* \def MI_RED
* Defines red color.
*/
#define MI_RED "FF0000"

/**
* \def MI_CYAN
* Defines cyan color.
*/
#define MI_CYAN "00FFFF"

/**
* \def MI_MAGENTA
* Defines magenta color.
*/
#define MI_MAGENTA "FF00FF"

/**
* \def MI_YELLOW
* Defines yellow color.
*/
#define MI_YELLOW "FFFF00"

/**
* \def MI_WHITE
* Defines white color.
*/
#define MI_WHITE "FFFFFF"

#ifndef MINIMUM
    #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAXIMUM
    #define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
#endif

#ifndef ABS
    #define ABS(a) ((a) >= (0) ? (a) : (-a))
#endif

namespace MoveIn
{
    /**
    * \class RGBColor
    *
    * \brief Stores pixel values in terms of red, green and blue.
    */
    class RGBColor
    {
        private:
        /**
        * \var unsigned short int R
        *
        * \brief The red value.
        */
        unsigned short int R;

        /**
        * \var unsigned short int G
        *
        * \brief The green value.
        */
        unsigned short int G;

        /**
        * \var unsigned short int B
        *
        * \brief The blue value.
        */
        unsigned short int B;

        /**
        * \fn RGBColor::charToInt(char*)
        *
        * \brief Converts an hexadecimal character to an integer value.
        *
        * \param digit
        * A C character varing from 0 to F.
        *
        * \return 0..15 to hexadecimal characters and -1 to invalid characters.
        */
        int charToInt(char digit);

        public:
        /**
        * \fn RGBColor::RGBColor(unsigned short int,
        *                        unsigned short int,
        *                        unsigned short int)
        *
        * \brief Constructor of the class. Default pixel value is black.
        *
        * \param r
        * Red value.
        *
        * \param g
        * Green value.
        *
        * \param b
        * Blue value.
        */
        RGBColor(unsigned short int r = 0,
                 unsigned short int g = 0,
                 unsigned short int b = 0);

        /**
        * \fn RGBColor::RGBColor(char *)
        *
        * \brief Constructor of the class. Creates RGB color by hexadecimal
        * parameter.
        *
        * \param color
        * Hexadecimal color.
        */
        RGBColor(char *color);

        /**
        * \fn RGBColor::~RGBColor()
        *
        * \brief Destructor of the class.
        */
        ~RGBColor();

        /**
        * \fn RGBColor::setColor(unsigned short int,
        *                        unsigned short int,
        *                        unsigned short int)
        *
        * \brief Sets the pixel value with positive integer parameters.
        *
        * \param r
        * Red value.
        *
        * \param g
        * Green value.
        *
        * \param b
        * Blue value.
        */
        void setColor(unsigned short int r,
                      unsigned short int g,
                      unsigned short int b);

        /**
        * \fn RGBColor::setColor(RGBColor*)
        *
        * \brief Sets the pixel value by cloning a class of the same type.
        *
        * \param color
        * A pointer to RGBColor class.
        */
        void setColor(RGBColor *color);

        /**
        * \fn RGBColor::setColor(RGBColor*)
        *
        * \brief Sets the pixel value by a string representing a web color.
        *
        * \param color
        * A C string representing a web color (hexadecimal value). Valid
        * parameters vary from "0x000000" to "0xFFFFFF" or from "000000" to
        * "FFFFFF". Lower case characters are also accepted.
        */
        void setColor(char *color);

        /**
        * \fn RGBColor::setR(unsigned short int)
        *
        * \brief Sets the pixel red value.
        *
        * \param r
        * Red value. -1 < r < 256.
        */
        void setR(unsigned short int r);

        /**
        * \fn RGBColor::setG(unsigned short int)
        *
        * \brief Sets the pixel green value.
        *
        * \param g
        * Green value. -1 < g < 256.
        */
        void setG(unsigned short int g);

        /**
        * \fn RGBColor::setB(unsigned short int)
        *
        * \brief Sets the pixel blue value.
        *
        * \param b
        * Blue value. -1 < b < 256.
        */
        void setB(unsigned short int b);

        /**
        * \fn RGBColor::getColor()
        *
        * \brief Returns a pointer to this class.
        *
        * \return "this" pointer.
        */
        RGBColor* getColor();

        /**
        * \fn RGBColor::getR()
        *
        * \brief Returns the red value.
        *
        * \return Red value.
        */
        unsigned short int getR();

        /**
        * \fn RGBColor::getG()
        *
        * \brief Returns the green value.
        *
        * \return Green value.
        */
        unsigned short int getG();

        /**
        * \fn RGBColor::getB()
        *
        * \brief Returns the blue value.
        *
        * \return Blue value.
        */
        unsigned short int getB();

        /**
        * \fn RGBColor::normalize()
        *
        * \brief Converts the RGB values to normalized values in the same
        * color space;
        */
        void normalize();
    };

/*******************************************************************************
* MoveIn::RGBColor::RGBColor                                                   *
*******************************************************************************/

    RGBColor::RGBColor(unsigned short int r,
                       unsigned short int g,
                       unsigned short int b)
    {
        R = r;
        G = g;
        B = b;
    }

/*******************************************************************************
* MoveIn::RGBColor::RGBColor                                                   *
*******************************************************************************/

    RGBColor::RGBColor(char *color)
    {
        setColor(color);
    }

/*******************************************************************************
* MoveIn::RGBColor::~RGBColor                                                  *
*******************************************************************************/

    RGBColor::~RGBColor()
    {
    }

/*******************************************************************************
* MoveIn::RGBColor::setColor                                                   *
*******************************************************************************/

    void RGBColor::setColor(unsigned short int r,
                         unsigned short int g,
                         unsigned short int b)
    {
        setR(r < 256 ? r : 255);
        setG(g < 256 ? g : 255);
        setB(b < 256 ? b : 255);
    }

/*******************************************************************************
* MoveIn::RGBColor::setColor                                                   *
*******************************************************************************/

    void RGBColor::setColor(RGBColor *color)
    {
        setR(color->getR());
        setG(color->getG());
        setB(color->getB());
    }

/*******************************************************************************
* MoveIn::RGBColor::setColor                                                   *
*******************************************************************************/

    void RGBColor::setColor(char *color)
    {
        if(strlen(color) == 6)
        {
            R = charToInt(color[0]) * 16 +
                charToInt(color[1]);
            G = charToInt(color[2]) * 16 +
                charToInt(color[3]);
            B = charToInt(color[4]) * 16 +
                charToInt(color[5]);
        }
        if(strlen(color) == 8)
        {
            R = charToInt(color[2]) * 16 +
                charToInt(color[3]);
            G = charToInt(color[4]) * 16 +
                charToInt(color[5]);
            B = charToInt(color[6]) * 16 +
                charToInt(color[7]);
        }
    }

/*******************************************************************************
* MoveIn::RGBColor::setR                                                       *
*******************************************************************************/

    void RGBColor::setR(unsigned short int r)
    {
        R = r < 256 ? r : 255;
    }

/*******************************************************************************
* MoveIn::RGBColor::setG                                                       *
*******************************************************************************/

    void RGBColor::setG(unsigned short int g)
    {
        G = g < 256 ? g : 255;
    }

/*******************************************************************************
* MoveIn::RGBColor::setB                                                       *
*******************************************************************************/

    void RGBColor::setB(unsigned short int b)
    {
        B = b < 256 ? b : 255;
    }

/*******************************************************************************
* MoveIn::RGBColor::getColor                                                   *
*******************************************************************************/

    RGBColor* RGBColor::getColor()
    {
        return this;
    }

/*******************************************************************************
* MoveIn::RGBColor::getR                                                       *
*******************************************************************************/

    unsigned short int RGBColor::getR()
    {
        return R;
    }

/*******************************************************************************
* MoveIn::RGBColor::getR                                                       *
*******************************************************************************/

    unsigned short int RGBColor::getG()
    {
        return G;
    }

/*******************************************************************************
* MoveIn::RGBColor::getR                                                       *
*******************************************************************************/

    unsigned short int RGBColor::getB()
    {
        return B;
    }

/*******************************************************************************
* MoveIn::RGBColor::charToInt                                                  *
*******************************************************************************/

    int RGBColor::charToInt(char digit)
    {
        switch(digit)
        {
            case 'a':
                return 10;
            case 'b':
                return 11;
            case 'c':
                return 12;
            case 'd':
                return 13;
            case 'e':
                return 14;
            case 'f':
                return 15;
            case 'A':
                return 10;
            case 'B':
                return 11;
            case 'C':
                return 12;
            case 'D':
                return 13;
            case 'E':
                return 14;
            case 'F':
                return 15;
            case '0':
                return 0;
            case '1':
                return 1;
            case '2':
                return 2;
            case '3':
                return 3;
            case '4':
                return 4;
            case '5':
                return 5;
            case '6':
                return 6;
            case '7':
                return 7;
            case '8':
                return 8;
            case '9':
                return 9;
        }
        return -1;
    }

/*******************************************************************************
* MoveIn::RGBColor::normalize                                                  *
*******************************************************************************/

    void RGBColor::normalize()
    {
        float r, g, b;
        r = R;
        g = G;
        b = B;

        if(R+G+B != 0)
        {
            r = r/(R+G+B);
            g = g/(R+G+B);
            b = b/(R+G+B);

            r *= 255;
            g *= 255;
            b *= 255;

            R = (unsigned short int) r;
            G = (unsigned short int) g;
            B = (unsigned short int) b;
        }
    }
}

#endif

